package com.migration.service.impl;

import com.migration.model.migration.AbstractMigrationScript;
import com.migration.model.migration.MigrationScript;
import com.migration.model.migration.CustomExecutableMigrationScript;
import com.migration.service.MigrationScriptService;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationContext;
import org.springframework.core.type.filter.AssignableTypeFilter;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * 迁移脚本服务实现类
 */
@Slf4j
@Service
public class MigrationScriptServiceImpl implements MigrationScriptService {

    // 新数据库连接
    private final JdbcTemplate newJdbcTemplate;

    // 旧数据库连接
    private final JdbcTemplate oldJdbcTemplate;
    
    // Spring应用上下文
    private final ApplicationContext applicationContext;

    // 使用ConcurrentHashMap存储所有的迁移脚本
    private final Map<String, MigrationScript> scriptMap = new ConcurrentHashMap<>();

    /**
     * 构造函数，同时注入新旧数据库的JdbcTemplate和ApplicationContext
     *
     * @param newJdbcTemplate 新库的JDBC模板
     * @param oldJdbcTemplate 旧库的JDBC模板
     * @param applicationContext Spring应用上下文
     */
    public MigrationScriptServiceImpl(
            @Qualifier("newJdbcTemplate") JdbcTemplate newJdbcTemplate,
            @Qualifier("oldJdbcTemplate") JdbcTemplate oldJdbcTemplate,
            ApplicationContext applicationContext) {
        this.newJdbcTemplate = newJdbcTemplate;
        this.oldJdbcTemplate = oldJdbcTemplate;
        this.applicationContext = applicationContext;
    }

    /**
     * 初始化方法，自动扫描并注册所有AbstractMigrationScript的子类
     */
    @PostConstruct
    public void init() {
        log.info("初始化迁移脚本...");

        // 首先尝试从Spring上下文中获取已注册的AbstractMigrationScript Bean
        try {
            Map<String, AbstractMigrationScript> scriptBeans = 
                applicationContext.getBeansOfType(AbstractMigrationScript.class);
            
            log.info("从Spring上下文中找到 {} 个迁移脚本Bean", scriptBeans.size());
            
            for (AbstractMigrationScript script : scriptBeans.values()) {
                registerScript(script);
                log.info("从Spring容器注册迁移脚本: {}", script.getName());
            }
        } catch (Exception e) {
            log.error("从Spring容器获取迁移脚本失败", e);
        }

        // 扫描并注册所有AbstractMigrationScript的子类
        try {
            // 创建扫描器，不使用默认过滤器
            ClassPathScanningCandidateComponentProvider provider = 
                new ClassPathScanningCandidateComponentProvider(false);
            
            // 添加过滤器，只查找AbstractMigrationScript的子类
            provider.addIncludeFilter(new AssignableTypeFilter(AbstractMigrationScript.class));
            
            // 获取基础包名
            String basePackage = "com.migration";
            
            // 扫描包
            Set<BeanDefinition> beanDefinitions = provider.findCandidateComponents(basePackage);
            
            for (BeanDefinition beanDefinition : beanDefinitions) {
                try {
                    // 加载类
                    Class<?> clazz = Class.forName(beanDefinition.getBeanClassName());
                    
                    // 跳过抽象类
                    if (java.lang.reflect.Modifier.isAbstract(clazz.getModifiers())) {
                        continue;
                    }
                    
                    // 如果类名已经存在于scriptMap中，说明已经通过Spring注册，跳过
                    String className = clazz.getSimpleName();
                    if (scriptMap.values().stream().anyMatch(s -> s.getClass().getSimpleName().equals(className))) {
                        log.debug("跳过已通过Spring注册的迁移脚本: {}", className);
                        continue;
                    }
                    
                    // 创建实例
                    Object instance = clazz.getDeclaredConstructor().newInstance();
                    
                    // 注册脚本
                    if (instance instanceof AbstractMigrationScript) {
                        registerScript((AbstractMigrationScript) instance);
                        log.info("自动注册迁移脚本: {}", ((AbstractMigrationScript) instance).getName());
                    }
                } catch (Exception e) {
                    log.error("初始化迁移脚本失败: {}", beanDefinition.getBeanClassName(), e);
                }
            }
        } catch (Exception e) {
            log.error("扫描迁移脚本失败", e);
        }

        log.info("已注册 {} 个迁移脚本", scriptMap.size());
    }

    @Override
    public List<MigrationScript> getAllScripts() {
        // 按order排序返回所有脚本
        return scriptMap.values().stream()
                .sorted(Comparator.comparing(MigrationScript::getOrder))
                .collect(Collectors.toList());
    }

    @Override
    public MigrationScript getScriptByName(String name) {
        return scriptMap.get(name);
    }

    @Override
    @Transactional
    public Map<String, Object> executeMigrationScript(String scriptName) {
        MigrationScript script = getScriptByName(scriptName);
        Map<String, Object> result = new HashMap<>();

        if (script == null) {
            log.error("未找到迁移脚本: {}", scriptName);
            result.put("success", false);
            result.put("message", "未找到迁移脚本: " + scriptName);
            return result;
        }

        try {
            log.info("开始执行迁移脚本: {}", script.getName());

            // 记录开始时间
            long startTime = System.currentTimeMillis();

            // 检查是否是自定义执行迁移脚本
            int rowsAffected = 0;
            if (script instanceof CustomExecutableMigrationScript) {
                log.info("检测到自定义迁移脚本，执行自定义迁移逻辑...");
                CustomExecutableMigrationScript customScript = (CustomExecutableMigrationScript) script;
                rowsAffected = customScript.executeCustomMigration(newJdbcTemplate.getDataSource());
            } else {
                // 执行清空表SQL
                log.info("执行清空表SQL: {}", script.getTruncateSql());
                newJdbcTemplate.execute(script.getTruncateSql());
                
                // 执行标准迁移SQL
                log.info("执行迁移SQL...");
                rowsAffected = newJdbcTemplate.update(script.getMigrationSql());
            }

            // 计算耗时
            long endTime = System.currentTimeMillis();
            long duration = endTime - startTime;

            log.info("迁移完成, 迁移数据 {} 条，耗时 {} 毫秒", rowsAffected, duration);

            // 返回结果
            result.put("success", true);
            result.put("message", "迁移成功");
            result.put("script", script.getName());
            result.put("description", script.getDescription());
            result.put("rowsAffected", rowsAffected);
            result.put("durationMs", duration);

            return result;
        } catch (Exception e) {
            log.error("执行迁移脚本失败: {}", script.getName(), e);
            result.put("success", false);
            result.put("message", "执行迁移脚本失败: " + e.getMessage());
            result.put("script", script.getName());
            return result;
        }
    }

    @Override
    @Transactional
    public Map<String, Object> executeAllMigrationScripts() {
        Map<String, Object> result = new HashMap<>();
        List<Map<String, Object>> details = new ArrayList<>();
        boolean allSuccess = true;
        int totalRowsAffected = 0;
        long totalDuration = 0;

        // 获取所有脚本，按顺序执行
        List<MigrationScript> scripts = getAllScripts();

        log.info("开始执行所有迁移脚本，共 {} 个", scripts.size());

        for (MigrationScript script : scripts) {
            Map<String, Object> scriptResult = executeMigrationScript(script.getName());
            details.add(scriptResult);

            if (!(boolean) scriptResult.get("success")) {
                allSuccess = false;
            } else {
                totalRowsAffected += (int) scriptResult.get("rowsAffected");
                totalDuration += (long) scriptResult.get("durationMs");
            }
        }

        result.put("success", allSuccess);
        result.put("message", allSuccess ? "所有迁移脚本执行成功" : "部分迁移脚本执行失败");
        result.put("totalScripts", scripts.size());
        result.put("totalRowsAffected", totalRowsAffected);
        result.put("totalDurationMs", totalDuration);
        result.put("details", details);

        return result;
    }

    @Override
    public void registerScript(MigrationScript script) {
        if (script == null || script.getName() == null) {
            log.error("无法注册脚本: 脚本或脚本名称为空");
            return;
        }

        log.info("注册迁移脚本: {}", script.getName());
        scriptMap.put(script.getName(), script);
    }

    @Override
    public List<Map<String, Object>> queryFromOldDatabase(String sql) {
        log.info("从旧数据库查询数据: {}", sql);
        try {
            return oldJdbcTemplate.queryForList(sql);
        } catch (Exception e) {
            log.error("从旧数据库查询数据失败", e);
            throw e;
        }
    }

    @Override
    public List<Map<String, Object>> queryFromNewDatabase(String sql) {
        log.info("从新数据库查询数据: {}", sql);
        try {
            return newJdbcTemplate.queryForList(sql);
        } catch (Exception e) {
            log.error("从新数据库查询数据失败", e);
            throw e;
        }
    }

    @Override
    public int updateNewDatabase(String sql) {
        log.info("更新新数据库: {}", sql);
        try {
            return newJdbcTemplate.update(sql);
        } catch (Exception e) {
            log.error("更新新数据库失败", e);
            throw e;
        }
    }

    @Override
    @Transactional
    public Map<String, Object> executeCustomMigration(String sourceSql,
                                                 java.util.function.Function<List<Map<String, Object>>, String> dataProcessor) {
        Map<String, Object> result = new HashMap<>();

        try {
            log.info("开始执行自定义跨库迁移");

            // 记录开始时间
            long startTime = System.currentTimeMillis();

            // 从旧库查询数据
            log.info("从旧库查询数据: {}", sourceSql);
            List<Map<String, Object>> sourceData = oldJdbcTemplate.queryForList(sourceSql);
            log.info("查询到 {} 条数据", sourceData.size());

            // 处理数据，生成目标SQL
            log.info("处理数据并生成目标SQL");
            String targetSql = dataProcessor.apply(sourceData);

            // 目标SQL为空时，直接返回成功
            if (targetSql == null || targetSql.trim().isEmpty()) {
                log.info("目标SQL为空，无需执行更新操作");
                result.put("success", true);
                result.put("message", "无需执行更新操作");
                result.put("sourceDataCount", sourceData.size());
                result.put("rowsAffected", 0);
                return result;
            }

            // 执行目标SQL
            log.info("执行目标SQL: {}", targetSql);
            int rowsAffected = newJdbcTemplate.update(targetSql);

            // 计算耗时
            long endTime = System.currentTimeMillis();
            long duration = endTime - startTime;

            log.info("自定义迁移完成, 迁移数据 {} 条，耗时 {} 毫秒", rowsAffected, duration);

            // 返回结果
            result.put("success", true);
            result.put("message", "自定义迁移成功");
            result.put("sourceDataCount", sourceData.size());
            result.put("rowsAffected", rowsAffected);
            result.put("durationMs", duration);

            return result;
        } catch (Exception e) {
            log.error("执行自定义迁移失败", e);
            result.put("success", false);
            result.put("message", "执行自定义迁移失败: " + e.getMessage());
            return result;
        }
    }
}
